#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#define SIZE 512
#define NUM 32
#define None_Redir 0
#define In_Redir 1
#define Out_Redir 2
#define App_Redir 3
int redir = None_Redir;
char* filename = NULL;
char* gargv[NUM];
#define skipspace(cmd,pos) while(cmd[pos]==' ')pos++
const char* GetUserName()
{
	const char* name = getenv("USER");
	if(name == NULL)
		return "None";
	else
		return name;
}

const char* GetHostName()
{
	const char* name = getenv("LOGNAME");
	if(name == NULL)
		return "None";
	else
		return name;
}

const char* Getcwd()
{
	const char* cwd = getenv("PWD");
	if(cwd  == NULL)
		return "None";
	else
		return cwd;
}
const char* GetHome()
{
	const char* home=getenv("HMOE");
	if(home == NULL)
		return "/";
	else
		return home;
}
void makecommand(char command[],size_t size)
{
	const char* usrname=GetUserName();
	const char* hostname=GetHostName();
	const char* pwd=Getcwd();
	snprintf(command,size,"[%s:%s %s]> ",usrname,hostname,pwd);
	printf("%s",command);
	fflush(stdout);
}
int GetUsrcommand(char* usrcommand,size_t size)
{
	char* s= fgets(usrcommand,size,stdin);
	if(s == NULL)
	{

	}
	else
	{
		usrcommand[strlen(usrcommand)-1] = '\0';
	//	printf("%s\n",usrcommand);
		return strlen(s);
	}
}
void SplitCommand(char command[],size_t size)
{
	gargv[0] = strtok(command," ");
	int index = 1;
	while(gargv[index++] = strtok(NULL," "));
}
void Execute()
{

	pid_t id = fork();
	if(id<0)
		exit(1);
	else if(id == 0)
	{
		if(redir != None_Redir)
		{
			if(redir == In_Redir)
			{
				int fd = open(filename,O_RDONLY);
				dup2(fd,0);
			}
			else if(redir == Out_Redir)
			{
				int fd = open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
				dup2(fd,1);
			}
			else
			{
				int fd = open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
				dup2(fd,1);
			}
		}
		execvp(gargv[0],gargv);
		printf("mistake!\n");
		exit(errno);
	}
	else
	{
		int status = 0;
		waitpid(id,&status,0);
	}
}
void CD()
{
	const char* path = gargv[1];
	const char* cwd;
	char pwd[SIZE*2];
	char tmp[SIZE*2];
	if(path == NULL || strcmp(path,"~") == 0)
	{
		cwd = GetHome();
	}
	else if(strcmp(path,"-") == 0)
	{
		cwd = "/";
	}
	else if(strcmp(path,"..") == 0)
	{
		cwd = Getcwd();
		int end = strlen(cwd)-1;
		while(cwd[end]!='/') end--;
		if(end==0)
			end=1;
		int start = 0;
		while(start<end)
		{
			tmp[start] = cwd[start];
			start++;
		}
	    tmp[end]='\0';	
		cwd=tmp;
	}
	else if(strcmp(path,".") == 0)
	{
		cwd = Getcwd();
	}
	else
	{
		if(path[0] == '/')
		{

			if(access(path,0)!=0)
			{
				printf("NO FOUND!\n");
				return;
			}
			cwd = path;
		}
		else
		{
			if(access(path,0)!=0)
			{
				printf("NO FOUND!\n");
				return;
			}
			cwd = Getcwd();
			if(strcmp(cwd,"/")==0)
				snprintf(tmp,sizeof(tmp),"/%s",path);
			else
			snprintf(tmp,sizeof(tmp),"%s/%s",cwd,path);
			cwd = tmp;
		}
	}
	chdir(cwd);
	snprintf(pwd,sizeof(pwd),"%s",cwd);
	setenv("PWD",pwd,1);
	//printf("%s\n",Getcwd());
}
int Check()
{
	const char* cmd = gargv[0];
	if(strcmp(cmd ,"cd")==0)
	{
		CD();
		return 1;
	}
	return 0;
}
void checkdir(char cmd[])
{
	int pos = 0;
	int end = strlen(cmd);
	redir = None_Redir;
	while(pos<end)
	{
		if(cmd[pos]=='<')
		{
			redir = In_Redir;
			cmd[pos]='\0';
			pos++;
			skipspace(cmd,pos);
			filename = cmd + pos;
			break;
		}
		else if(cmd[pos]=='>')
		{
			if(cmd[pos + 1] == '>')
			{
				redir = App_Redir;
				cmd[pos] = '\0';
				pos+=2;
				skipspace(cmd,pos);
				filename = cmd + pos;
				break;
			}
			redir = Out_Redir;
			cmd[pos] = '\0';
			pos++;
			skipspace(cmd,pos);
			filename = cmd + pos;
			break;
		}
		pos++;
	}
}
int main()
{
	char command[SIZE];
	int quit=1;
	while(quit)
	{
		//输出一个命令行
		makecommand(command,sizeof(command));
		char usrcommand[SIZE];
		//用户输入指令
		int n = GetUsrcommand(usrcommand,sizeof(usrcommand));
		if(n <= 0)
			return 1; 
		//检查重定向
		checkdir(usrcommand);
		//分割指令
		SplitCommand(usrcommand,sizeof(command));
		//检测内建命令
		n = Check();
		//执行指令
		if(!n)
		Execute();	
	}

	sleep(3);

	return 0;
}
